#include "phononoutput.h"
#include <QDebug>
#include <QVBoxLayout>
#include <QAudioOutput>
#include <QAudioDeviceInfo>
#include <QtCore/qmath.h>
#include <QtCore/qendian.h>
#include "audiooutput.h"

const int DurationSeconds = 1;
const int DataFrequencyHz = 44100;
const int BufferSize      = 32768;

PhononOutput::PhononOutput()
    :   m_pullTimer(new QTimer(this))
    ,   m_deviceBox(0)
    ,   m_device(QAudioDeviceInfo::defaultOutputDevice())
    ,   m_generator(0)
    ,   m_audioOutput(0)
    ,   m_output(0)
    ,   m_buffer(BufferSize, 0)
{
    initializeWindow();
    initializeAudio();
}

void PhononOutput::initializeWindow()
{
    qWarning() << "Inicializando janelas...";
    QScopedPointer<QWidget> window(new QWidget);
    window->setWindowTitle("AudioFreq");
    QScopedPointer<QVBoxLayout> layout(new QVBoxLayout);

    // Devices ComboBox

    comboLabel = new QLabel(tr("<p> <align=center> <b> Dispositivos Disponiveis (Phonon): </b></p>"));
    layout->addWidget(comboLabel);

    m_deviceBox = new QComboBox(this);

    audioOutputDevices = Phonon::BackendCapabilities::availableAudioOutputDevices();

    foreach (Phonon::AudioOutputDevice p_device, audioOutputDevices ){
        m_deviceBox->addItem(p_device.name());
    }

    connect(m_deviceBox,SIGNAL(activated(int)),SLOT(set_p_device(int)));

    layout->addWidget(m_deviceBox);

    // Start / Stop Button

    startstopbutton = new QPushButton(this);
    startstopbutton->setText("Iniciar Som");
    connect(startstopbutton, SIGNAL(clicked()), this, SLOT(start_stop_sound_phonon()));


    // Slider With frequencies (integer numbers) and Spin Box
    // Both are inserted in a QHBoxLayout

    slider = new QSlider(Qt::Horizontal);
    slider->setRange(250, 8000);
    slider->setValue(500);

    IncreaseVolume = new QPushButton(this);
    IncreaseVolume->setText(tr("Aumentar 1 db"));

    DecreaseVolume = new QPushButton(this);
    DecreaseVolume->setText(tr("Diminuir 1 db"));

    VLayout = new QVBoxLayout;
    VLayout->addWidget(IncreaseVolume);
    VLayout->addWidget(DecreaseVolume);

    Hbox_buttons = new QHBoxLayout;
    layout->addLayout(Hbox_buttons);
    Hbox_buttons->addWidget(startstopbutton);
    Hbox_buttons->addLayout(VLayout);

    connect(IncreaseVolume, SIGNAL(clicked()), this, SLOT(increase_db()));
    connect(DecreaseVolume, SIGNAL(clicked()), this, SLOT(decrease_db()));

    Hbox = new QHBoxLayout;
    layout->addLayout(Hbox);

    slider_value = new QSpinBox;
    slider_value->setRange(250, 8000);
    slider_value->setValue(slider->value());

    ToneFrequencyHz = slider->value();

    Hbox->addWidget(slider);
    Hbox->addWidget(slider_value);

    connect(slider, SIGNAL(valueChanged(int)), this, SLOT(update_frequency()));
    connect(slider_value, SIGNAL(valueChanged(int)), slider, SLOT(setValue(int)));

    volume_dial = new QDial(this);
    volume_dial->setMinimum(-30);
    volume_dial->setMaximum(30);
    layout->addWidget(volume_dial);


    // Other Stuff...

    window->setLayout(layout.data());
    layout.take(); // ownership transferred

    setCentralWidget(window.data());
    QWidget *const windowPtr = window.take(); // ownership transferred
    windowPtr->show();
}

void PhononOutput::update_frequency(){
    qWarning() << "Old Frequency: " << ToneFrequencyHz;
    ToneFrequencyHz = this->slider->value();
    this->slider_value->setValue(slider->value());
    qWarning() << "New Frequency: " << ToneFrequencyHz;
    this->initializeAudio();
}

void PhononOutput::start_stop_sound_phonon(){
    if (p_m_object->state() == Phonon::StoppedState){
        p_m_object->stop();
        p_m_object->play();
        qWarning() << "Volume = " << p_audiooutput->volume();
        qWarning() << "Volume db = " << p_audiooutput->volumeDecibel();
        startstopbutton->setText("Parar Som");
        qWarning() << "Phonon State: " << p_m_object->state();
    }

    else if (p_m_object->state() == Phonon::PlayingState){
        p_m_object->stop();
        startstopbutton->setText("Iniciar Som");
        qWarning() << "Phonon State: " << p_m_object->state();
    }

    else if (p_m_object->state() == Phonon::LoadingState){
        startstopbutton->setText("Carregando...");
    }

    else if (p_m_object->state() == Phonon::ErrorState){
    	qWarning() << "Error Type: " << p_m_object->errorType();
    }

    else if (p_m_object->state() == Phonon::BufferingState){
    	qWarning() << "Buffering....";
    }
}


void PhononOutput::initializeAudio(){
    qWarning() << "Inicializando audio...";
    connect(m_pullTimer, SIGNAL(timeout()), SLOT(pullTimerExpired()));

    m_pullMode = true;

    m_format.setFrequency(DataFrequencyHz);
    m_format.setChannels(2);
    m_format.setSampleSize(16);
    m_format.setCodec("audio/pcm");
    m_format.setByteOrder(QAudioFormat::LittleEndian);
    m_format.setSampleType(QAudioFormat::SignedInt);

    QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());

    if (!info.isFormatSupported(m_format)) {
        qWarning() << "Default format not supported - trying to use nearest";
        m_format = info.nearestFormat(m_format);
    }

    m_generator = new Generator(m_format, DurationSeconds*1000000, ToneFrequencyHz, this);
    createAudioOutput();

}

void PhononOutput::createAudioOutput()
{
    qWarning() << "Criando saida de audio...";
//    delete p_audiooutput;
//    p_audiooutput = 0;
    m_generator->start();

//Begining of Phonon Audio Output



    p_m_object = new Phonon::MediaObject(this);
    p_ado = new Phonon::AudioDataOutput(p_m_object);
//    p_m_object->setCurrentSource(Phonon::MediaSource("./test/250Hz.wav"));
    qWarning()<< "Definido fonte de audio...";
    p_m_object->setCurrentSource(Phonon::MediaSource(m_generator));
    qWarning()<< "Feito!";
    p_audiooutput = new Phonon::AudioOutput(this);
    p_audiooutput->setOutputDevice(audioOutputDevices[0]);
    Phonon::createPath(p_m_object, p_audiooutput);
    connect(startstopbutton, SIGNAL(clicked()), this, SLOT(start_stop_sound_phonon()));
    connect(p_m_object, SIGNAL(stateChanged(Phonon::State,Phonon::State)), this, SLOT(warn_state(Phonon::State, Phonon::State)));

    connect(volume_dial, SIGNAL(sliderMoved(int)), this, SLOT(increase_db(int)));

}

PhononOutput::~PhononOutput()
{
}

void PhononOutput::notified()
{
    qWarning() << "bytesFree = " << m_audioOutput->bytesFree()
               << ", " << "elapsedUSecs = " << m_audioOutput->elapsedUSecs()
               << ", " << "processedUSecs = " << m_audioOutput->processedUSecs();
}

void PhononOutput::pullTimerExpired()
{
    if (m_audioOutput && m_audioOutput->state() != QAudio::StoppedState) {
        int chunks = m_audioOutput->bytesFree()/m_audioOutput->periodSize();
        while (chunks) {
           const qint64 len = m_generator->read(m_buffer.data(), m_audioOutput->periodSize());
           if (len)
               m_output->write(m_buffer.data(), len);
           if (len != m_audioOutput->periodSize())
               break;
           --chunks;
        }
    }
}


void PhononOutput::set_p_device(int i){
    p_audiooutput->setOutputDevice(audioOutputDevices[i]);
}



void PhononOutput::warn_state(Phonon::State old_state, Phonon::State new_state){
    qWarning() << "Phonon Media Object State Changes State " << old_state
               << " to " << new_state;
}



void PhononOutput::increase_db(){
    if (p_m_object->state() == Phonon::PlayingState){
        p_m_object->stop();
        p_audiooutput->setVolumeDecibel(p_audiooutput->volumeDecibel() + 1.0);
        volume_dial->setValue(p_audiooutput->volumeDecibel());
        p_m_object->play();
    }
    else {
        p_audiooutput->setVolumeDecibel(p_audiooutput->volumeDecibel() + 1.0);
        volume_dial->setValue(p_audiooutput->volumeDecibel());
    }
}

void PhononOutput::increase_db(int i){
    if (p_m_object->state() == Phonon::PlayingState){
        p_m_object->stop();
        p_audiooutput->setVolumeDecibel(p_audiooutput->volumeDecibel() + (i * 1.0));
        volume_dial->setValue(p_audiooutput->volumeDecibel());
        p_m_object->play();
    }
    else {
        p_audiooutput->setVolumeDecibel(p_audiooutput->volumeDecibel() + (i* 1.0));
        volume_dial->setValue(p_audiooutput->volumeDecibel());
    }
}

void PhononOutput::decrease_db(){
    if (p_m_object->state() == Phonon::PlayingState){
        p_m_object->stop();
        p_audiooutput->setVolumeDecibel(p_audiooutput->volumeDecibel() - 1.0);
        volume_dial->setValue(p_audiooutput->volumeDecibel());
        qWarning() << "Volume = " << p_audiooutput->volume();
        qWarning() << "Volume db = " << p_audiooutput->volumeDecibel();
        p_m_object->play();
    }
    else {
        p_audiooutput->setVolumeDecibel(p_audiooutput->volumeDecibel() - 1.0);
        volume_dial->setValue(p_audiooutput->volumeDecibel());
    }
}

void PhononOutput::decrease_db(int i){
    if (p_m_object->state() == Phonon::PlayingState){
        p_m_object->stop();
        p_audiooutput->setVolumeDecibel(p_audiooutput->volumeDecibel() - (i * 1.0));
        volume_dial->setValue(p_audiooutput->volumeDecibel());
        qWarning() << "Volume = " << p_audiooutput->volume();
        qWarning() << "Volume db = " << p_audiooutput->volumeDecibel();
        p_m_object->play();
    }
    else {
        p_audiooutput->setVolumeDecibel(p_audiooutput->volumeDecibel() - (i * 1.0));
        volume_dial->setValue(p_audiooutput->volumeDecibel());
    }
}

